/**************************************************************************
 *
 * Copyright (C) 2007 University of California, Los Angeles.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 *
 *************************************************************************/

/**
 * 
 */
package edu.ucla.cs.typecast.discovery;

import java.io.IOException;
import java.net.InetAddress;
import java.util.Hashtable;
import java.util.LinkedList;
import java.util.ListIterator;
import java.util.Vector;

import edu.ucla.cs.typecast.event.EventListener;
import edu.ucla.cs.typecast.event.EventManager;
import edu.ucla.cs.typecast.net.EndPoint;
import edu.ucla.cs.typecast.net.TypeCastConstants;
import edu.ucla.cs.typecast.rmi.TypeCastClient;
import edu.ucla.cs.typecast.rmi.TypeCastServer;

/**
 *
 * @date Aug 7, 2007
 */
public class ServiceDiscoveryManager implements  TypeCastConstants, ServiceAdvertisementListener {
	private static ServiceDiscoveryManager instance = new ServiceDiscoveryManager(); 
//	private LinkedList<DiscoveryRegistrationEntry> entries = new LinkedList<DiscoveryRegistrationEntry>(); 
	private Hashtable<EndPoint, LinkedList<DiscoveryRegistrationEntry>> listeningEndPoints = new Hashtable<EndPoint, LinkedList<DiscoveryRegistrationEntry>>(); 
	
	
	public static ServiceDiscoveryManager getInstance() { 
		return instance; 
	}

	private ServiceDiscoveryManager() { 
	}
	
	public synchronized void addDiscoveryListener(ServiceTemplate template, ServiceDiscoveryListener listener) throws IOException  { 
		addDiscoveryListener(EndPoint.DEFAULT_RMI_ENDPOINT, template, listener); 
	}

	public synchronized void addDiscoveryListener(EndPoint endPoint, ServiceTemplate template, ServiceDiscoveryListener listener) throws IOException  { 
		DiscoveryRegistrationEntry entry = new DiscoveryRegistrationEntry(listener, template); 
		LinkedList<DiscoveryRegistrationEntry> entries = listeningEndPoints.get(endPoint); 
		if (entries == null) { 
			entries = new LinkedList<DiscoveryRegistrationEntry>(); 
			entries.add(entry); 
		}
		listeningEndPoints.put(endPoint, entries); 
		if (entries.size() == 1) { 			
			TypeCastServer server = new TypeCastServer(endPoint); 			
			server.export(ServiceAdvertisementListener.class, this); 
		}
	}
	
	public synchronized void removeDiscoveryListener(ServiceDiscoveryListener listener) { 
		for(EndPoint endPoint :  listeningEndPoints.keySet()) { 
			LinkedList<DiscoveryRegistrationEntry> entries = listeningEndPoints.get(endPoint); 
			ListIterator<DiscoveryRegistrationEntry> i = entries.listIterator();
			while(i.hasNext()) { 
				DiscoveryRegistrationEntry entry   = i.next();   
				if (entry.getListener() == listener) { 
					i.remove(); 
				}
			}
			if (entries.size() == 0) { 
				try { 
					TypeCastServer server = new TypeCastServer(endPoint); 
					server.unexport(this); 
				} catch(Throwable t) { 
					t.printStackTrace(); 
				}				
			}
		}
		
	}
	
	public ServiceDiscoveryService getDiscoveryClient() throws IOException  {  		
		TypeCastClient tclient = new TypeCastClient(); 
		return tclient.getInvocationHandler(ServiceDiscoveryService.class); 
	}

	public ServiceDiscoveryService getDiscoveryClient(InetAddress addr , int port) throws IOException  { 
		TypeCastClient tclient = new TypeCastClient(addr, port); 
		return tclient.getInvocationHandler(ServiceDiscoveryService.class); 
	}
	
	public synchronized void notify(ServiceAdvertisement serviceInfo) { 
			ServiceAdvertisement sa = serviceInfo; 
			for(EndPoint endPoint :  listeningEndPoints.keySet()) { 
				LinkedList<DiscoveryRegistrationEntry> entries = listeningEndPoints.get(endPoint); 
				ListIterator<DiscoveryRegistrationEntry> i = entries.listIterator();
				while(i.hasNext()) { 
					DiscoveryRegistrationEntry entry   = i.next();   
					if (entry.getTemplate().match(sa)) { 
						entry.getListener().notify(sa); 
					}
				}
			}
	}
		
}
